namespace TwitterButtler.RequestServer.WakeOnLan
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.ComponentModel.Composition;
	using System.Windows.Threading;

	using Infrastructure;

	using RequestServers;

	#endregion

	[Export(typeof(IButtlerRequestServer))]
	public class WakeupMachineButtlerRequestServer : MachinePowerStatusButtlerRequestServerBase,
	                                                 IButtlerRequestServer
	{
		private readonly List<BackgroundWorker> backgroundWorkersList = new List<BackgroundWorker>();

		#region IButtlerRequestServer Members

		public bool CanPerformRequest(string userName, string requestText)
		{
			requestText = requestText.Trim();

			return requestText.StartsWith("wake up", StringComparison.InvariantCultureIgnoreCase) ||
			       requestText.StartsWith("wakeup", StringComparison.InvariantCultureIgnoreCase);
		}

		public void PerformRequest(DateTime originalRequestDateTime, string requestText, string screenName, Action<string, string> onRequestCompleted)
		{
			WakeOnLanUserEntry entry;
			if (this.userNameToMacAddressDictionary.TryGetValue(screenName, out entry))
			{
				bool isAwake = false;

				if (!string.IsNullOrEmpty(entry.MachineName))
				{
					isAwake = Ping(entry, 0);
				}

				WakeOnLanMessage.WakeUp(entry.MachineMacAddress);

				if (isAwake)
				{
					onRequestCompleted(screenName, "The machine was already awake.");
				}
				else
				{
					if (string.IsNullOrEmpty(entry.MachineName))
					{
						string replyMessage = string.Format("The '{1}' request you sent on {0} was performed, but could not be verified.", originalRequestDateTime,
						                                    requestText);
						onRequestCompleted(screenName, replyMessage);
					}
					else
					{
						var backgroundWorker = new BackgroundWorker();
						backgroundWorker.DoWork += OnPingInBackground;
						backgroundWorker.RunWorkerCompleted += OnFinishedPingInBackground;
						backgroundWorker.RunWorkerAsync(new PingParams(entry, requestText, originalRequestDateTime, onRequestCompleted));
						this.backgroundWorkersList.Add(backgroundWorker);
					}
				}
			}
			else
			{
				string replyMessage = string.Format("You don't have your machine configured in the config file.", screenName);

				onRequestCompleted(screenName, replyMessage);
			}
		}

		#endregion

		private void OnPingInBackground(object sender, DoWorkEventArgs e)
		{
			PingParams pingParams = null;

			try
			{
				pingParams = (PingParams) e.Argument;

				e.Result = Ping(pingParams.Entry, 30);

				this.dispatcher.BeginInvoke(
					(Action)
					(() =>
					 SendReply(pingParams.Entry.UserName, pingParams.OriginalRequestDateTime, (bool) e.Result, pingParams.RequestText, pingParams.OnRequestCompleted)),
					DispatcherPriority.Normal);
			}
			catch (Exception ex)
			{
				e.Result = ex;

				if (pingParams != null)
				{
					this.dispatcher.BeginInvoke((Action) (() => pingParams.OnRequestCompleted(pingParams.Entry.UserName, ex.Message)), DispatcherPriority.Normal);
				}
			}
		}

		private void OnFinishedPingInBackground(object sender, RunWorkerCompletedEventArgs e)
		{
			var backgroundWorker = (BackgroundWorker) sender;

			this.dispatcher.BeginInvoke((Action) (() =>
			                                      {
			                                      	backgroundWorker.Dispose();
			                                      	this.backgroundWorkersList.Remove(backgroundWorker);
			                                      }), DispatcherPriority.Background);
		}
	}
}